using System;
using System.IO;
using Drill.Configuration;
using Drill.Core.Utilities;
using Drill.Extensibility;

namespace Drill.Core
{
	public class ConfigBasedConfigurationSourceFactory : IConfigurationSourceFactory
	{
		private readonly IConfigSourceConfiguration _configSourceConfig;
		private readonly IConfigurationHelper _configurationHelper;

		public ConfigBasedConfigurationSourceFactory(IConfigSourceConfiguration configSourceConfig, IConfigurationHelper configurationHelper = null)
		{
			if (null == configSourceConfig)
			{
				throw new ArgumentNullException("configSourceConfig");
			}
			_configSourceConfig = configSourceConfig;
			_configurationHelper = configurationHelper;
		}

		public IConfigurationSource CreateConfigurationSource()
		{
			DrillBitConfigMethod configurationMethod = _configSourceConfig.Method;
			ConfigurationSource configurationSource = null;

			switch (configurationMethod)
			{
				case DrillBitConfigMethod.ConfigFile:
					configurationSource = CreateConfigFileConfigurationSource();
					break;

				case DrillBitConfigMethod.ConfigFileWithCustomLocation:
					configurationSource = CreateConfigFileWithCustomLocationConfigurationSource();
					break;

				case DrillBitConfigMethod.DrillBitSetup:
					configurationSource = CreateDrillBitSetConfigurationSource();
					break;

				case DrillBitConfigMethod.Default:
					configurationSource = CreateDefaultConfigurationSource();
					break;

				case DrillBitConfigMethod.Xml:
					configurationSource = CreateXmlConfigurationSource();
					break;

				case DrillBitConfigMethod.XmlFile:
					configurationSource = CreateXmlFileConfigurationSource();
					break;
			}

			return configurationSource;
		}

		private ConfigurationSource CreateConfigFileConfigurationSource()
		{
			return new ConfigurationSource
			{
				ConfigurationMethod = DrillBitConfigMethod.ConfigFile
			  , ConfigurationObject = null
			  , Key = _configSourceConfig.Key
			};
		}

		private ConfigurationSource CreateConfigFileWithCustomLocationConfigurationSource()
        {
            // The configuration item value represents the configuration file name.
            string configuredFilename = _configSourceConfig.Value;
			string filename = null;

			if (false == string.IsNullOrWhiteSpace(configuredFilename) && (false == Path.IsPathRooted(configuredFilename) || false == File.Exists(configuredFilename)))
			{
				filename = _configurationHelper.FindAbsolutePath(configuredFilename);
			}

            return new ConfigurationSource
            {
                ConfigurationMethod = DrillBitConfigMethod.ConfigFileWithCustomLocation
			  , ConfigurationObject = filename ?? configuredFilename
			  , Key = _configSourceConfig.Key
            };
        }

		private ConfigurationSource CreateDrillBitSetConfigurationSource()
		{
			// The configuration item value is the assembly-qualified type name 
			// of the type that implements IDrillBitSetup.
			string assemblyQualifiedTypeName = _configSourceConfig.Value;

			IDrillBitSetup configurationProvider =
				AssemblyUtility.CreateInstance<IDrillBitSetup>(
					assemblyQualifiedTypeName
				  , "DrillBit setup"
				  , new object[] { }
					);
			
			if (null == configurationProvider)
			{
				throw new DependencyResolverException(
					"An instance of the DrillBit setup assembly-qualified type {0} could not be created. "
				  + "The DrillBit cannot be configured."
				  , assemblyQualifiedTypeName
					);
			}

			return new ConfigurationSource
            {
                ConfigurationMethod = DrillBitConfigMethod.DrillBitSetup
              , ConfigurationObject = configurationProvider
			  , Key = _configSourceConfig.Key
            };
		}

		private ConfigurationSource CreateDefaultConfigurationSource()
        {
            return new ConfigurationSource
            {
                ConfigurationMethod = DrillBitConfigMethod.Default
              , ConfigurationObject = null
			  , Key = _configSourceConfig.Key
            };
        }

        private ConfigurationSource CreateXmlConfigurationSource()
        {
            // The configuration item value represents the literal XML string.
            string xml = _configSourceConfig.Value;

            return new ConfigurationSource
            {
                ConfigurationMethod = DrillBitConfigMethod.ConfigFileWithCustomLocation
              , ConfigurationObject = xml
			  , Key = _configSourceConfig.Key
            };
        }

		private ConfigurationSource CreateXmlFileConfigurationSource()
        {
            // The configuration item value represents the XML file name.
			string configuredFilename = _configSourceConfig.Value;
			string filename = null;

			if (false == string.IsNullOrWhiteSpace(configuredFilename) && (false == Path.IsPathRooted(configuredFilename) || false == File.Exists(configuredFilename)))
			{
				filename = _configurationHelper.FindAbsolutePath(configuredFilename);
			}

            return new ConfigurationSource
            {
                ConfigurationMethod = DrillBitConfigMethod.XmlFile
              , ConfigurationObject = filename ?? configuredFilename
			  , Key = _configSourceConfig.Key
            };
        }
    }
}